{
 * UHTMLReporter.pas
 *
 * Defines classes that generates an HTML report about some given version
 * information.
 *
 * $Rev: 191 $
 * $Date: 2011-03-30 18:02:35 +0000 (Wed, 30 Mar 2011) $
 *
 * ***** BEGIN LICENSE BLOCK *****
 * 
 * Version: MPL 1.1
 * 
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 * 
 * The Original Code is UHTMLReporter.pas.
 * 
 * The Initial Developer of the Original Code is Peter Johnson
 * (http://www.delphidabbler.com/).
 * 
 * Portions created by the Initial Developer are Copyright (C) 2004-2011 Peter
 * Johnson. All Rights Reserved.
 * 
 * Contributor(s):
 *   NONE
 *
 * ***** END LICENSE BLOCK *****
}


unit UHTMLReporter;


interface


uses
  // Delphi
  Windows,
  // Project
  IntfVerInfoReport, IntfVerInfoReader,
  UHTMLWriter, UReporter, UTextStreamWriter;

type

  {
  THTMLReporter:
    Writes an HTML format descriptive report about given version information to
    an output stream or file.
  }
  THTMLReporter = class(TReporter, IVerInfoReporter3)
  private
    procedure ReportHeadingText(const Writer: THTMLWriter;
      const Headings: string);
      {Writes the text of the given headings as strongly emphasised text to the
      stream. Separate heading lines are written as separate paragraphs}
    procedure ReportFixedFileInfo(const Writer: THTMLWriter;
      const FFI: TVSFixedFileInfo);
      {Writes the given fixed file information out in a table}
    procedure ReportVarFileInfo(const Writer: THTMLWriter;
      const VarInfo: IVerInfoVarReader);
      {Writes the variable file information for given translation to stream.
      Details of translation are written as a heading and the string table is
      written to a table. Any inconsistencies are highlighted}
  protected
    procedure DoReport(const VI: IVerInfoReader;
      const Writer: TTextStreamWriter; const Header: WideString); override;
      {Reads version information using given reader and writes a report on it,
      including given header, as HTML to the given stream}
  end;


implementation


uses
  // Delphi
  SysUtils, Classes,
  // Project
  UReportConsts, UDisplayFmt, UVerUtils;


resourcestring
  // THTMLReporter specific strings
  sHTMLTitle = 'Version Information Spy Report';
  sHTMLMainHeading = 'Version Information Report';
  sHTMLFFIHeading = 'Fixed File Information';
  sHTMLVFIHeading = 'Variable File Information';
  sHTMLCredits = 'This report was generated by Version Information Spy from %s';
  sHTMLTranslation = 'Translation: %0.4X-%0.4X';
  sHTMLTransInfo = 'Translation information';
  sHTMLLanguage = 'Language';
  sHTMLCharset = 'Character set';
  sHTMLStrTable = 'String table';
  sHTMLEmptyStrTable = 'No entries in string table';
  sHTMLMissingStrTable =
    'There is no string table associated with this translation';
  sHTMLMissingTrans = 'There is no translation entry for this translation';


const
  // Help A-Link keywords
  cFFIHelp = 'FFIHelp';
  cStrTableHelp = 'StrTableHelp';


{ THTMLReporter }

procedure THTMLReporter.DoReport(const VI: IVerInfoReader;
  const Writer: TTextStreamWriter; const Header: WideString);
  {Reads version information using given reader and writes a report on it,
  including given header, as HTML to the given stream}
var
  Idx: Integer;             // loops thru all translations in variable file info
  HTMLWriter: THTMLWriter;  // HTML writer object that wraps text stream writer
begin
  HTMLWriter := THTMLWriter.Create(Writer);
  try
    // Write head HTML
    HTMLWriter.WriteHeader(sHTMLTitle);
    // Open body
    HTMLWriter.WriteBodyStart;
    // Write main heading and header text
    HTMLWriter.WritePara(tgHeading1, '', sHTMLMainHeading);
    ReportHeadingText(HTMLWriter, Header);
    // Fixed file info: we include link into help file
    HTMLWriter.WritePara(
      tgHeading2,
      HTMLFillBackgroundStyle,
      HTMLHelpLink(cFFIHelp) + sHTMLFFIHeading
    );
    ReportFixedFileInfo(HTMLWriter, VI.FixedFileInfo);
    // Variable file info
    HTMLWriter.WritePara(tgHeading2, HTMLFillBackgroundStyle, sHTMLVFIHeading);
    for Idx := 0 to Pred(VI.VarInfoCount) do
      ReportVarFileInfo(HTMLWriter, VI.VarInfo(Idx));
    // Close body
    HTMLWriter.WriteBodyEnd;
    // Close document
    HTMLWriter.WriteFooter;
  finally
    HTMLWriter.Free;
  end;
end;

procedure THTMLReporter.ReportFixedFileInfo(const Writer: THTMLWriter;
  const FFI: TVSFixedFileInfo);
  {Writes the given fixed file information out in a table}
begin
  // Begin table for fixed file info
  Writer.WriteTableStart;

  // Write each FFI item on a row of table with rule between each cell
  // write file and product information
  Writer.WriteTableSepRow(2);
  Writer.WriteTableRow(
    [HTMLEnclose(tgEmphasised, sFileVersion),
    UDisplayFmt.VerFmt(FFI.dwFileVersionMS, FFI.dwFileVersionLS)]
  );
  Writer.WriteTableSepRow(2);
  Writer.WriteTableRow(
    [HTMLEnclose(tgEmphasised, sProductVersion),
      UDisplayFmt.VerFmt(FFI.dwProductVersionMS, FFI.dwProductVersionLS)]
  );
  // write file flags and mask as descriptions
  Writer.WriteTableSepRow(2);
  Writer.WriteTableRow(
    [HTMLEnclose(tgEmphasised, sFileFlagsMask),
      UVerUtils.FileFlagsDesc(FFI.dwFileFlagsMask, dtDesc)]
  );
  Writer.WriteTableSepRow(2);
  Writer.WriteTableRow(
    [HTMLEnclose(tgEmphasised, sFileFlags),
      UVerUtils.FileFlagsDesc(FFI.dwFileFlags, dtDesc)]
  );
  // write file OS
  Writer.WriteTableSepRow(2);
  Writer.WriteTableRow(
    [HTMLEnclose(tgEmphasised, sFileOS),
      UVerUtils.FileOSDesc(FFI.dwFileOS, dtDesc)]
  );
  // write file type
  Writer.WriteTableSepRow(2);
  Writer.WriteTableRow(
    [HTMLEnclose(tgEmphasised, sFileType),
      UVerUtils.FileTypeDesc(FFI.dwFileType, dtDesc)]
  );
  // write file sub type if required
  Writer.WriteTableSepRow(2);
  if UVerUtils.FileTypeHasSubType(FFI.dwFileType) then
    // we have a sub type
    Writer.WriteTableRow(
      [HTMLEnclose(tgEmphasised, sFileSubType),
        UVerUtils.FileSubTypeDesc(FFI.dwFileType, FFI.dwFileSubType, dtDesc)]
    )
  else
    // no sub type: say so
    Writer.WriteTableRow([HTMLEnclose(tgEmphasised, sFileSubType), sNa]);
  // write date
  Writer.WriteTableSepRow(2);
  Writer.WriteTableRow(
    [HTMLEnclose(tgEmphasised, sCreateDate),
      UDisplayFmt.DateFmt(FFI.dwFileDateMS, FFI.dwFileDateLS)]
  );

  // rule off FFI table
  Writer.WriteTableSepRow(2);

  // Close the FFI table
  Writer.WriteTableEnd;
end;

procedure THTMLReporter.ReportHeadingText(const Writer: THTMLWriter;
  const Headings: string);
  {Writes the text of the given headings as strongly emphasised text to the
  stream. Separate heading lines are written as separate paragraphs}
var
  Lines: TStringList; // string list storing lines of heading
  Idx: Integer;       // loops thru lines of heading
begin
  // Split headings into lines by assigning to string list
  Lines := TStringList.Create;
  try
    Lines.Text := Headings;
    // Write out each line of heading as strongly emphasised text
    for Idx := 0 to Pred(Lines.Count) do
      Writer.WritePara(tgPara, '', HTMLEnclose(tgStrong, Lines[Idx]));
  finally
    Lines.Free;
  end;
end;

procedure THTMLReporter.ReportVarFileInfo(const Writer: THTMLWriter;
  const VarInfo: IVerInfoVarReader);
  {Writes the variable file information for given translation to stream. Details
  of translation are written as a heading and the string table is written to a
  table. Any inconsistencies are highlighted}
var
  Idx: Integer;             // loops thru strings in string table
  StrTableCols: Integer;    // number of columns in string table
  TransTableCols: Integer;  // number of columns in tranlation table
begin
  // Write out translation header with lang/charset codes
  Writer.WritePara(
    tgHeading3,
    '',
    Format(sHTMLTranslation, [VarInfo.LanguageID, VarInfo.CharSet])
  );

  // Write the translation info
  // record how many cols in table
  if VarInfo.Status = VARVERINFO_STATUS_STRTABLEONLY then
    TransTableCols := 1   // one col for error message
  else
    TransTableCols := 2;  // two cols: item and value
  // write char set and language in a table
  Writer.WriteTableStart;
  // .. table header line & ruling
  Writer.WriteTableHeader(sHTMLTransInfo, TransTableCols);
  Writer.WriteTableSepRow(TransTableCols);
  if VarInfo.Status = VARVERINFO_STATUS_STRTABLEONLY then
  begin
    // .. error: no trans table - display little bomb to indicate problem
    Writer.WriteTableRow([
      HTMLBombImage + HTMLEnclose(tgEmphasised, sHTMLMissingTrans)
    ]);
    Writer.WriteTableSepRow(1);
  end
  else
  begin
    // .. valid translation entry - display language and charset info
    Writer.WriteTableRow(
      [HTMLEnclose(tgEmphasised, sHTMLLanguage),
        LanguageDesc(VarInfo.LanguageID)]
    );
    Writer.WriteTableSepRow(2);
    Writer.WriteTableRow(
      [HTMLEnclose(tgEmphasised, sHTMLCharset),
        CharSetDesc(VarInfo.CharSet)]
    );
    Writer.WriteTableSepRow(2);
  end;
  // .. close translation info
  Writer.WriteTableEnd;

  // Write break to separate translation info from string table
  Writer.WriteBreak;

  // Write string table info
  // record how many cols in string table
  if (VarInfo.Status = VARVERINFO_STATUS_TRANSONLY)
    or (VarInfo.StringCount = 0) then
    StrTableCols := 1   // we have no entries to write out
  else
    StrTableCols := 2;  // 2 cols for string names/values
  // write table containing string table or comments
  Writer.WriteTableStart;
  Writer.WriteTableHeader(  // we have link to string table in help file
    HTMLHelpLink(cStrTableHelp) + sHTMLStrTable, StrTableCols
  );
  Writer.WriteTableSepRow(StrTableCols);
  if VarInfo.Status = VARVERINFO_STATUS_TRANSONLY then
  begin
    // .. error: no string table  - display little bomb to indicate problem
    Writer.WriteTableRow(
      [HTMLBombImage + HTMLEnclose(tgEmphasised, sHTMLMissingStrTable)]
    );
    Writer.WriteTableSepRow(1);
  end
  else
  begin
    if (VarInfo.StringCount = 0) then
    begin
      // .. there are no entries in string table
      Writer.WriteTableRow([HTMLEnclose(tgEmphasised, sHTMLEmptyStrTable)]);
      Writer.WriteTableSepRow(1);
    end
    else
    begin
      // .. we have entries in string table - list them
      for Idx := 0 to Pred(VarInfo.StringCount) do
      begin
        Writer.WriteTableRow(
          [HTMLEnclose(tgEmphasised, VarInfo.StringName(Idx)),
          VarInfo.StringValue(Idx)]
        );
        Writer.WriteTableSepRow(2);
      end;
    end;
  end;
  // .. close table
  Writer.WriteTableEnd;
end;

end.
